package com.zwcl.common.core.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;

/**
 * 序列号生成工具
 *
 * @author xyp
 * @create 2019-8-1
 */
@Slf4j
public class SequenceUtil {

    public static DateTimeFormatter YYMMDD = DateTimeFormatter.ofPattern("yyMMdd");
    public static DateTimeFormatter YYMMDDHHMMSS = DateTimeFormatter.ofPattern("yyMMddHHmmss");
    public static DateTimeFormatter YYMMDDHH = DateTimeFormatter.ofPattern("yyMMddHH");
    /**
     * 订单日期标志位 判断流水号是否需要从1开始
     */
    private static long orderDay = Long.valueOf(YYMMDD.format(LocalDateTime.now()));

    private static final String SEQUENCE_SERIAL_DATE = "sequence:number:date";//TODO 不要和会员权益一样
    private static final String SEQUENCE_LOCKED_KEY = "sequence:lock";
    private static final String SEQUENCE_SERIAL_NUMBER = "sequence:number:incr";
    private static final long SEQUENCE_LOCKED_TIME_OUT = 30;//同步锁超时时间，单位：秒

    /*
     * 根据服务器的MAC地址生成1-9的编号 private static int serverNo; static { InetAddress address; try { address = InetAddress.getLocalHost(); NetworkInterface ni
     * = NetworkInterface.getByInetAddress(address); byte[] mac = ni.getHardwareAddress(); String sMAC = null; Formatter formatter = new Formatter();
     * for (int i = 0; i < mac.length; i++) { sMAC = formatter.format(Locale.getDefault(), "%02X%s", mac[i], ( i < mac.length - 1 ) ? "-" :
     * "").toString(); } if (StringUtils.isBlank(sMAC)) { sMAC = address.getHostAddress(); } serverNo = ( Math.abs(sMAC.hashCode()) % 9 + 1 ); } catch
     * (Exception e) { // TODO Auto-generated catch block serverNo = 1; } }
     */

    /**
     * 生成订单号
     * <p>
     * 生成规则 ：(1位)订单来源 + (2位)订单类型 + (1位)交易类型 + (8位)日期 + (7位)流水号
     * 订单来源：1-小程序
     * 订单类型：EtcOrderPrefixEnum SequenceTypeEnum
     * 交易类型：1-支付，2-流水
     * 日期：yyMMddHH
     * 流水号(每天从1开始 , 例如:0000001)
     *
     * @param origin    订单来源
     * @param orderType 订单类型
     * @param tradeType 交易类型
     * @return
     */
    private static String genOrderNo(int origin, String orderType, int tradeType) {
        StringBuffer sb = new StringBuffer();
        // 订单来源
        sb.append(origin);
        // 订单类型
        sb.append(StringUtils.leftPad(orderType, 2, "0"));
        // 交易类型
        sb.append(tradeType);
        // 服务器编号
        // sb.append(serverNo);
        // 日期
        sb.append(YYMMDDHH.format(LocalDateTime.now()));
        // 流水号
        sb.append(getOrderBaseSerialNum(8));
        return sb.toString();
    }

    /**
     * 生成流水号 TODO 之后整理，定义业务类型和支付、退款
     *
     * @param type 订单类型
     * @return
     */
    public static String generateNo(Integer type) {
        return genOrderNo(1, type.toString(), 2);
    }

    /**
     * 获取自增量
     *
     * @param len 自增量长度
     * @return
     */
    public static String getOrderBaseSerialNum(int len) {
        //静态方法中拿取对象
        StringRedisTemplate stringRedisTemplate = SpringUtils.getBean("stringRedisTemplate");
        long nowDay = Long.valueOf(YYMMDD.format(LocalDateTime.now()));
        if (nowDay > orderDay) {
            boolean isLock = false;
            String orderNoDate = null;
            try {
                do {
                    isLock = stringRedisTemplate.opsForValue().setIfAbsent(SEQUENCE_LOCKED_KEY, "lock", SEQUENCE_LOCKED_TIME_OUT, TimeUnit.SECONDS);
                } while (!isLock);
                if (nowDay > orderDay) {
                    orderNoDate = (String) stringRedisTemplate.opsForValue().get(SEQUENCE_SERIAL_DATE);
                    // 是否需要更新生产订单的日期和订单编号重新计数
                    boolean isUpdateGenOrderConfig = false;
                    if (StringUtils.isNotBlank(orderNoDate)) {
                        if (Long.valueOf(orderNoDate).longValue() < nowDay) {
                            isUpdateGenOrderConfig = true;
                        } else if (Long.valueOf(orderNoDate).longValue() == nowDay) {
                            orderDay = nowDay;
                        }
                    } else {
                        isUpdateGenOrderConfig = true;
                    }

                    if (isUpdateGenOrderConfig) {
                        orderDay = nowDay;
                        stringRedisTemplate.opsForValue().set(SEQUENCE_SERIAL_DATE, String.valueOf(nowDay));
                        stringRedisTemplate.opsForValue().set(SEQUENCE_SERIAL_NUMBER, "0");
                    }
                }
            } finally {
                stringRedisTemplate.delete(SEQUENCE_LOCKED_KEY);
            }
        }

        // redis生产 订单流水号
        String serialNum = stringRedisTemplate.opsForValue().increment(SEQUENCE_SERIAL_NUMBER).toString();
        log.info("订单流水号生成,time:{},redis incr:{}", YYMMDDHHMMSS.format(LocalDateTime.now()), serialNum);
        return StringUtils.leftPad(serialNum, len, "0");
    }

}
